/**
 * 复数对象类
 * 如果复数虚部是0，数学意义数值是实数，但该实例对象仍然是一个Complex对象类型，而非Number类型
 * @param real 复数的实部，必须
 * @param imaginary 复数的虚部，必须
 * @param suffix  复数的后缀，i或j，必须。
 */
class Complex{
    constructor(real , imaginary, suffix){
        //确保两个实参都是数字
        if( !isNumber(real) || !isNumber(imaginary)){
            throw new TypeError();        //如果不都是数字则抛出错误
        }
        //检查后缀
        if(!(suffix === 'i' || suffix === 'j')){
            throw new TypeError();        //如果不是i或者j则抛出错误
        }
        this.r = real
        this.i = imaginary
        this.s = suffix || 'i'
    }
    /**
     * 将复数对象装换为一个字符串
     * @returns {string}
     */
    toString(){
        if(this.r ===0) {
            if (this.i === 0) {
                return '0'
            }
            if (this.i === 1) {
                return this.s
            }
            if (this.i > 1) {
                return this.i+this.s
            }
            if (this.i === -1) {
                return '-'+this.s
            }
            if (this.i < -1) {
                return this.i+this.s
            }

        } else{
            if( this.i === 0){
                return this.r
            }
            if( this.i === 1){
                return this.r+'+'+this.s
            }
            if( this.i === -1){
                this.i = '-'
                return this.r+'-'+this.s
            }
            if( this.i > 1){
                return this.r+'+'+this.i+this.s
            }
            if(this.i < -1){
                return this.r+''+this.i+this.s
            }
        }
    }

}

/**
 * 复数的操作对象：
 * 公有方法：
 * set:创建一个复数，返回值是一个Complex实例对象
 * sum:参数可以是复数，也可以是实数，返回两个或者多个复数（实数）之和，返回值是一个Complex实例对象。
 * sub:参数可以是复数，也可以是实数，返回两个复数（实数）只差，返回值是一个Complex实例对象。
 * mul:参数可以是复数，也可以是实数，返回两个或多个复数（实数）乘积，返回值是一个Complex实例对象。
 * div:参数可以是复数，也可以是实数，返回两个复数（实数）商，返回值是一个Complex实例对象。
 * con:参数必须是复数，返回复数的共轭复数。
 * abs:参数可以是复数，也可以是实数，返回复数（实数）的模，返回值是一个实数，Number类型。
 *
 * 工具方法：
 * is:判断一个对象是否是Complex实例对象,若是返回true，否则返回false
 */
const ComplexType={
    /**
     * 创建一个复数对象，返回值是complex实例对象类型
     * @param real 实部
     * @param imaginary 虚部
     * @param suffix 后缀i或者j
     * @returns {Complex}
     */
    set(real , imaginary, suffix){
        return new Complex(real , imaginary, suffix)
    },
    /**
     * @param complex
     * @returns {boolean}
     */
    is(complex){
        return complex.__proto__ === Complex.prototype
    },
    /**
     * 处理方法的参数（顺序保持），保证所有参数都是Complex对象
     * @param args 必须至少两个，可以多个 复数或者实数 复数或者实数
     * @returns {Array}
     */
    _handleParamter(...args){
        //保存所有复数对象
        let complexArr = []
        //复数后缀
        let s=''
        //参数不能呢为空
        if(args.length < 2){
            throw new TypeError('Parameters must be at least two, and can be multiple complex or real')
        }
        //获取参数中复数的后缀
        args.forEach( item=>{
            if(this.is(item)){
                s += item.s
            }
        })
        //判断复数的后缀混，如果同时存在i和j，则抛出异常
        if(s.indexOf('i') !== -1 && s.indexOf('j') !== -1){
            throw Error('复数后缀必须统一，要么是i，要么是j ')
        }
        //重新设置复数后缀,如果参数都为实数，s设置为'i'，否则取第一位
        s = s.length?s.slice(0,1):'i'
        //将所有的复数追加到数组complexArr，如果是实数，则先将实数转为虚部为0的复数再进行
        args.forEach( item=>{
            //如果是complex对象，直接追加到数组complexArr
            if(this.is(item)){
                complexArr.push(item)
            }
            //如果是Number类型，先转为Complex对象，再追加到数组complexArr
            else if(isNumber(item)){
                complexArr.push(this.set(item,0,s))
            }else {//否则抛出异常
                throw new TypeError('The first  parameter is not allowed')
            }
        })
        return complexArr
    },
    /**
     * 复数乘法方法，返回参数中所有复数依次相乘的值，返回值是一个Complex对象
     * @param args 必须至少两个，可以多个 复数或者实数 复数或者实数
     * @returns {*|Complex}
     */
    mul(...args){
        //处理方法的参数（顺序保持），保证所有参数都是Complex对象
        let complexArr =this._handleParamter(...args)

        //进行复数乘法运算
         let r = complexArr[0].r*complexArr[1].r-complexArr[0].i*complexArr[1].i
         let i =  complexArr[0].r*complexArr[1].i+complexArr[0].i*complexArr[1].r
         let s = complexArr[0].s
         let result = this.set(r,i,s)

        //如果有三个或者以上参数，依次进行乘法运算
          for(let k=2;k<complexArr.length ;k++){
              r = result.r*complexArr[k].r - result.i*complexArr[k].i
              i =  result.r*complexArr[k].i + result.i*complexArr[k].r
              result = this.set(r,i,s)
          }
        return result
    }
}

/***************工具方法**************************/
/**
 * 判断一个参数是否是数值类型
 * @param value
 * @returns {boolean}
 */
function isNumber(value){
    return typeof value === 'number' && !isNaN(value)
}




let a =  ComplexType.set(1,1,'j')
let b =  ComplexType.set(2,2,'j')
let c =  ComplexType.set(-2,-2,'j')
let result = null


//测试(1+j)*(2+2j)
result = ComplexType.mul(a,b)
console.log(result); // Complex { r: 0, i: 4, s: 'j' }
console.log('a*b toString:',result.toString(),'\n') //4j

//测试(1+j)*(2+2j)*(-2-2j)
result = ComplexType.mul(a,b,c)
console.log(result); // Complex { r: 8, i: -8, s: 'j' }
console.log('a*b*c toString:',result.toString(),'\n') //8-8j


//测试(1+j)*(2+2j)*(-2-2j)*2
result = ComplexType.mul(a,b,c,2)
console.log(result); // Complex { r: 16, i: -16, s: 'j' }
console.log('a*b*c*2 toString:',result.toString(),'\n') //16-16j

//测试1*2*3
result = ComplexType.mul(1,2,3)
console.log(result); // Complex { r: 6, i: 0, s: 'i' }
console.log('1*2*3 toString:',result.toString(),'\n') //6

ComplexType.mul(1)